let s:task = {}
let s:LOG = api#import('logger')
let s:MSG = api#import('message')

function! s:task.on_success(data) dict
    call s:MSG.debug("task::on_success(".string(a:data).")")
endfunction

function! s:task.on_error(error) dict
    call s:MSG.debug("task::on_error(".string(a:error).")")
endfunction

function! s:task.notify(data, error) dict
    call s:LOG.debug("task::notify(".string(a:data).",".string(a:error).")")
    if !empty(a:data)
        call self.on_success(a:data)
    endif
    if !empty(a:error) 
        call self.on_error(a:error)
    endif
    return self
endfunction

function! s:task.run(...) dict
    if a:0 > 0
        let l:engine = a:1
    else
        let l:engine = api#engine#get()
    endif
    let l:data = {}
    let l:error = {}
    if has_key(self, 'function') && (type(self.function) == v:t_func || type(self.function) == v:t_string)
        try
            let l:args = has_key(self, 'arguments') ? self.arguments : []
            let l:data = call(self.function, l:args)
            return self.on_success(l:data)
        catch
            let l:error = v:exception
            return self.on_error(l:error)
        endtry
    endif
    if has_key(self, 'statement') && type(self.statement) == v:t_string
        try
            let l:silent = has_key(self, 'silent') ? self.silent : v:false
            let l:data = execute(self.statement, l:silent)
            return self.on_success(l:data)
        catch
            let l:error = v:exception
            return self.on_error(l:error)
        endtry
    endif
    call s:LOG.debug("what's wrong with " . string(self))
    return self
endfunction

function! s:task.wait(...) dict
    if a:0 > 0
        let l:engine = a:1
    else
        let l:engine = api#engine#get()
    endif
    call l:engine.append(self)
    return self
endfunction

function! api#task#async(func, ...)
    let l:task = copy(s:task)
    if type(a:func) == v:t_string && !exists('*' . a:func)
        let l:task.statement = a:func
    endif
    if type(a:func) == v:t_func || type(a:func) == v:t_string && exists('*' . a:func)
        let l:task.function = a:func
        if a:0 > 0
            let l:task.arguments = a:000
        endif
    endif
    if type(a:func) == v:t_dict
        if type(l:task) == v:t_dict
            let l:task = extend(l:task, a:func, "force")
            call s:MSG.debug("calling async")
        else
            echoerr 'Should not come here.'
        endif
    endif
    return l:task
endfunction
